#!/usr/bin/env python
# -*- coding: utf-8 -*-

from farado.items.board_column import BoardColumn
from farado.items.message import Message
from farado.logger import logger
from farado.items.project import Project
from farado.items.version import Version
from farado.items.board import Board
from farado.items.issue import Issue
from farado.items.issue_change import IssueChange
from farado.items.field import Field
from farado.items.file import File
from farado.items.user import User
from farado.items.role import Role
from farado.items.rule import Rule
from farado.items.workflow import Workflow
from farado.items.state import State
from farado.items.issue_kind import IssueKind
from farado.items.field_kind import FieldKind, ValueTypes
from farado.items.user_role import UserRole
from farado.general_manager_holder import gm_holder
from farado.item_manager import ItemManager
from farado.file_manager import FileManager
from farado.helpers.html_escaper import html_escape_over_code_blocks
from farado.helpers.issue_statistics_helper import IssueStatisticsHelper
from farado.helpers.user_statistics_helper import UserStatisticsHelper
from farado.helpers.board_statistics_helper import BoardStatisticsHelper
from farado.general_manager_holder import permission_manager
from farado.general_manager_holder import meta_item_manager
from farado.general_manager_holder import raw_querier



class ProjectManager(ItemManager):
    def __init__(self):
        super().__init__()
        self.file_manager = FileManager()
        self.safe_value = html_escape_over_code_blocks
        self.issue_statistics_helper = IssueStatisticsHelper()

    def permission_manager(self):
        return permission_manager()

    def meta_item_manager(self):
        return meta_item_manager()

    def raw_querier(self):
        return raw_querier()

    def user_statistics_helper(self, user):
        return UserStatisticsHelper(user)

    def board_statistics_helper(self, board):
        return BoardStatisticsHelper(board)

    def project_caption(self, project_id):
        if project := self.project(project_id):
            return project.caption
        return ""

    def project_issues(self, project_id):
        if project_id:
            project_id = int(project_id)
            return gm_holder.meta_item_manager.items_by_value(Issue, "project_id", project_id)
        return []

    def project_issues_count(self, project_id):
        if project_id:
            project_id = int(project_id)
            return gm_holder.meta_item_manager.issues_count(projects_ids=[project_id])
        return []

    def boards(self):
        return gm_holder.meta_item_manager.items(Board)

    def board(self, board_id):
        if board_id:
            board_id = int(board_id)
            return gm_holder.meta_item_manager.item_by_id(Board, board_id)
        return None

    def board_columns_by_board(self, board_id):
        if board_id:
            board_id = int(board_id)
            return gm_holder.meta_item_manager.items_by_value(BoardColumn, "board_id", board_id)
        return []

    def board_columns(self):
        return gm_holder.meta_item_manager.items(BoardColumn)

    def board_column(self, board_column_id):
        if board_column_id:
            return gm_holder.meta_item_manager.item_by_id(BoardColumn, board_column_id)
        return None

    def roles(self):
        return gm_holder.meta_item_manager.items(Role)

    def role(self, role_id):
        if role_id:
            role_id = int(role_id)
            return gm_holder.meta_item_manager.item_by_id(Role, role_id)
        return None

    def rules_by_role(self, role_id):
        if role_id:
            role_id = int(role_id)
            return gm_holder.meta_item_manager.items_by_value(Rule, "role_id", role_id)
        return []

    def rule(self, rule_id):
        if rule_id:
            rule_id = int(rule_id)
            return gm_holder.meta_item_manager.item_by_id(Rule, rule_id)
        return None

    def user_roles_by_user(self, user_id):
        if user_id:
            user_id = int(user_id)
            return gm_holder.meta_item_manager.items_by_value(UserRole, "user_id", user_id)
        return []

    def roles_by_user(self, user_id):
        if user_id:
            user_id = int(user_id)
            return gm_holder.meta_item_manager.roles_by_user(user_id)
        return []

    def new_states_for_issue_id(self, issue_id):
        if not issue_id:
            return []
        return self.new_states_for_issue(self.issue(int(issue_id)))

    def new_states_for_issue(self, issue):
        if not issue:
            return []
        issue_kind = self.issue_kind(issue.issue_kind_id)
        if not issue_kind:
            return []
        workflow_id = issue_kind.workflow_id
        edges = self.edges_by_workflow_id(workflow_id)
        if not edges:
            return []
        states_ids = [edge.to_state_id for edge in edges if issue.state_id == edge.from_state_id]
        return sorted(
            [state for state in self.states_by_workflow_id(workflow_id) if state.id in states_ids],
            key=lambda state: state.order if state.order else 0
        )

    def progress_value(self, issue_id):
        sub_issues = self.sub_issues(issue_id)
        result = []
        if sub_issues:
            for sub_issue in sub_issues:
                if state := self.state(sub_issue.state_id):
                    result.append(state.weight_percentage())
        elif issue := self.issue(issue_id):
            if state := self.state(issue.state_id):
                result.append(state.weight_percentage())
        if not result:
            return 0
        return int(sum(result)/len(result))

    def value_types(self):
        return ValueTypes

    def issues(self):
        return gm_holder.meta_item_manager.items(Issue)

    def issue(self, issue_id):
        if issue_id:
            issue_id = int(issue_id)
            return gm_holder.meta_item_manager.item_by_id(Issue, issue_id)
        return None

    def sub_issues(self, issue_id):
        if issue_id:
            issue_id = int(issue_id)
            return gm_holder.meta_item_manager.items_by_value(Issue, 'parent_id', issue_id)
        return []

    def sub_issues_in_deep(self, issue_id):
        sub_issues = self.sub_issues(issue_id)
        for sub_issue in sub_issues:
            sub_issues += self.sub_issues_in_deep(sub_issue.id)
        return sub_issues

    def sub_issues_leaves(self, issue):
        sub_issues = self.sub_issues(issue.id)
        if not sub_issues:
            return [issue]

        result = []
        for sub_issue in sub_issues:
            result += self.sub_issues_leaves(sub_issue)
        return result

    def issues_by_state(self, state_id):
        if state_id:
            state_id = int(state_id)
            return gm_holder.meta_item_manager.items_by_value(Issue, 'state_id', state_id)
        return []

    def issues_by_ids(self, issues_ids):
        if issues_ids:
            return gm_holder.meta_item_manager.items_by_ids(
                item_type=Issue,
                ids=issues_ids,
            )
        return []

    def filtered_issues(
            self,
            kinds_ids=[],
            states_ids=[],
            parents_ids=[],
            projects_ids=[],
            versions_ids=[],
            ):
        count, issues = gm_holder.meta_item_manager.issues(
            kinds_ids=kinds_ids,
            states_ids=states_ids,
            parents_ids=parents_ids,
            projects_ids=projects_ids,
            versions_ids=versions_ids,
        )
        return issues

    def parent_issues(self, parent_issue_id):
        if parent_issue_id:
            parent_issue_id = int(parent_issue_id)
            issue = gm_holder.meta_item_manager.item_by_id(Issue, parent_issue_id)
            if issue:
                return [issue] + self.parent_issues(issue.parent_id)
        return []

    def issues_sub_issues_comments(self, issue_id):
        issue = self.issue(issue_id)
        if not issue:
            return []

        comments = issue.comments
        for sub_issue in self.sub_issues(issue_id):
            comments += sub_issue.comments

        return sorted(
            comments,
            key=lambda comment: comment.creation_datetime)

    def issues_comments_in_deep(self, issue_id):
        issue = self.issue(issue_id)
        if not issue:
            return []

        comments = issue.comments
        for sub_issue in self.sub_issues_in_deep(issue_id):
            comments += sub_issue.comments

        return sorted(
            comments,
            key=lambda comment: comment.creation_datetime)

    def issue_change(self, issue_change_id):
        if issue_change_id:
            issue_change_id = int(issue_change_id)
            return gm_holder.meta_item_manager.item_by_id(
                IssueChange,
                issue_change_id
            )
        return None

    def issue_changes_by_ids(self, issue_changes_ids):
        if issue_changes_ids:
            return gm_holder.meta_item_manager.items_by_ids(
                item_type=IssueChange,
                ids=issue_changes_ids,
            )
        return []

    def ordered_users(self, first_user_id=None):
        users = sorted(
            self.users(),
            key=lambda user: f'{user.is_blocked} {user.last_name} {user.first_name}',
        )
        if first_user_id:
            users = [user for user in users if not user.id == first_user_id]
            users = [self.user(first_user_id)] + users
        return users

    def user_by_id(self, id):
        return self.user(id)

    def user_by_login(self, login):
        if not login:
            return None
        for user in self.users():
            if login == user.login:
                return user
        return None

    def message(self, id):
        if not id:
            return None
        id = int(id)
        return gm_holder.meta_item_manager.item_by_id(Message, id)

    def messages_by_sender(self, user_id):
        if not user_id:
            return []
        user_id = int(user_id)
        messages = gm_holder.meta_item_manager.items_by_value(
            item_type=Message,
            value_name='sender_id',
            value=user_id,
        )
        return sorted(messages, key=lambda message: message.id, reverse=True)

    def messages_by_receiver(self, user_id):
        if not user_id:
            return []
        user_id = int(user_id)
        messages = gm_holder.meta_item_manager.items_by_value(
            item_type=Message,
            value_name='receiver_id',
            value=user_id,
        )
        return sorted(messages, key=lambda message: message.id, reverse=True)

    def unread_messages_by_receiver(self, user_id):
        if not user_id:
            return []
        user_id = int(user_id)
        messages = gm_holder.meta_item_manager.items_by_values(
            item_type=Message,
            values={
                'receiver_id': user_id,
                'was_read': False,
            },
        )
        return sorted(messages, key=lambda message: message.id, reverse=True)

    def unread_messages_by_sender(self, user_id):
        if not user_id:
            return []
        user_id = int(user_id)
        messages = gm_holder.meta_item_manager.items_by_values(
            item_type=Message,
            values={
                'sender_id': user_id,
                'was_read': False,
            },
        )
        return sorted(messages, key=lambda message: message.id, reverse=True)

    def have_unread_messages_by_user(self, user_id):
        if not user_id:
            return False
        user_id = int(user_id)
        return not bool(
            None == gm_holder.meta_item_manager.item_by_values(
                item_type=Message,
                values={
                    'receiver_id': user_id,
                    'was_read': False,
                },
            )
        )

    def create_issue(self, issue_kind_id):
        issue_kind = self.issue_kind(issue_kind_id)
        if not issue_kind:
            return None

        issue = Issue()
        issue.issue_kind_id = issue_kind.id
        issue.state_id = issue_kind.default_state_id
        for field_kind in self.field_kinds_by_issue_kind_id(issue_kind.id):
            issue.fields.append(Field(field_kind_id=field_kind.id))
        return issue

    def remove_file(self, file):
        if not file or not type(file) == File:
            return

        self.file_manager.remove_uploaded_file(file.path, file.name)
        self.remove_item(File, file.id)

    # TODO : Определить место для такого рода вспомогательных функций.
    def time_period(self, timedelta):
        return _time_period(timedelta, '{d} д {h} ч {m} мин {s} сек')

def _time_period(delta, pattern):
    data = {'d': delta.days}
    data['h'], rem = divmod(delta.seconds, 3600)
    data['m'], data['s'] = divmod(rem, 60)
    return pattern.format(**data)
